<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TextFSM 解析工具</title>
    <link rel="stylesheet" href="/static/style.css">
    <!-- 引入 FontAwesome 图标库 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
    <div class="container">
        <!-- 主题切换按钮 -->
        <button id="themeToggle" class="theme-toggle" aria-label="切换主题">
            <i class="fas fa-moon"></i>
        </button>

        <!-- 输入区域 -->
        <section class="input-section" aria-labelledby="inputHeading">
            <h2 id="inputHeading">输入文本</h2>
            <textarea id="inputText" placeholder="请输入需要解析的文本（最大10MB）" aria-label="输入文本"></textarea>
            <div class="checkbox-group">
                <label><input type="checkbox" id="ignoreTimestamp"> 忽略时间戳</label>
                <input type="text" id="timestampRegex" placeholder="输入正则表达式" value="^\[\d{8}_\d{6}\]" aria-label="时间戳正则表达式">
                <label><input type="checkbox" id="debugMode"> 调试模式</label>
            </div>
        </section>

        <!-- 模板管理区域 -->
        <section class="template-section" aria-labelledby="templateHeading">
            <h2 id="templateHeading">模板管理</h2>
            <div class="template-row">
                <select id="templateCategory" aria-label="模板分类">
                    <option value="">所有分类</option>
                    {% for template in templates | unique(attribute='category') %}
                        <option value="{{ template.category }}">{{ template.category }}</option>
                    {% endfor %}
                </select>
                <select id="templateSelect" aria-label="选择模板">
                    <option value="">选择已有模板</option>
                    {% for template in templates %}
                        <option value="{{ template.name }}" data-category="{{ template.category }}">{{ template.name }} ({{ template.category }})</option>
                    {% endfor %}
                </select>
            </div>
            <textarea id="templateText" placeholder="请输入或编辑模板内容" aria-label="模板内容"></textarea>
            <div class="template-controls">
                <input type="text" id="templateName" placeholder="模板名称" aria-label="模板名称">
                <input type="text" id="templateCategoryInput" placeholder="分类（可选）" aria-label="模板分类">
                <button id="saveTemplate"><i class="fas fa-save"></i> 保存</button>
                <button id="deleteTemplate"><i class="fas fa-trash"></i> 删除</button>
            </div>
        </section>

        <!-- 结果区域 -->
        <section class="result-section" aria-labelledby="resultHeading">
            <h2 id="resultHeading">解析结果</h2>
            <div id="result" aria-live="polite">请在上方输入文本和模板，结果将实时更新</div>
            <div class="export-buttons">
                <button id="exportExcel"><i class="fas fa-file-excel"></i> 导出Excel</button>
            </div>
            <div id="exportStatus" class="export-status"></div>
        </section>

        <!-- 页脚 -->
        <footer class="footer">
            <p>© 2025 Jack-Chen | <a href="#" aria-label="关于工具">TextFSM 解析工具</a></p>
        </footer>

        <!-- 密码模态框 -->
        <div id="passwordModal" class="modal" role="dialog" aria-hidden="true">
            <div class="modal-content">
                <h3 id="modalTitle">请输入密码</h3>
                <input type="password" id="passwordInput" aria-label="密码">
                <div class="modal-buttons">
                    <button id="modalConfirm">确认</button>
                    <button id="modalCancel">取消</button>
                </div>
            </div>
        </div>

        <!-- 加载指示器 -->
        <div id="loading" class="loading" aria-hidden="true">
            <i class="fas fa-spinner fa-spin"></i> 处理中...
        </div>
    </div>

    <script>
        // 核心变量
        const elements = {
            inputText: document.getElementById('inputText'),
            templateText: document.getElementById('templateText'),
            templateCategory: document.getElementById('templateCategory'),
            templateSelect: document.getElementById('templateSelect'),
            templateName: document.getElementById('templateName'),
            templateCategoryInput: document.getElementById('templateCategoryInput'),
            saveTemplateBtn: document.getElementById('saveTemplate'),
            deleteTemplateBtn: document.getElementById('deleteTemplate'),
            result: document.getElementById('result'),
            exportExcelBtn: document.getElementById('exportExcel'),
            exportStatus: document.getElementById('exportStatus'),
            ignoreTimestamp: document.getElementById('ignoreTimestamp'),
            timestampRegex: document.getElementById('timestampRegex'),
            debugMode: document.getElementById('debugMode'),
            themeToggle: document.getElementById('themeToggle'),
            passwordModal: document.getElementById('passwordModal'),
            passwordInput: document.getElementById('passwordInput'),
            modalConfirm: document.getElementById('modalConfirm'),
            modalCancel: document.getElementById('modalCancel'),
            loading: document.getElementById('loading')
        };
        let latestData = null;
        let modalCallback = null;

        // 工具函数
        const debounce = (func, wait) => {
            let timeout;
            return (...args) => {
                clearTimeout(timeout);
                timeout = setTimeout(() => func.apply(this, args), wait);
            };
        };

        const sanitizeInput = input => input.replace(/[<>"'`;]/g, '');

        const showLoading = () => elements.loading.style.display = 'flex';
        const hideLoading = () => elements.loading.style.display = 'none';

        const showModal = (title, callback) => {
            elements.passwordModal.style.display = 'flex';
            document.getElementById('modalTitle').textContent = title;
            elements.passwordInput.value = '';
            modalCallback = callback;
            elements.passwordInput.focus();
        };

        const hideModal = () => {
            elements.passwordModal.style.display = 'none';
            modalCallback = null;
        };

        const showError = message => {
            elements.result.innerHTML = `<p class="error">${sanitizeInput(message)}</p>`;
            elements.result.classList.add('error');
        };

        // 表格生成，List值用换行符分隔
        const createTable = (headers, rows) => {
            if (!headers || !rows.length) return '<p>无数据</p>';
            const numHeaders = headers.length;
            const firstColWidth = '10%';
            const headerColWidth = (90 / numHeaders) + '%';
            let table = '<table style="width: 100%; table-layout: fixed;">';
            table += '<thead><tr><th style="width: ' + firstColWidth + ';">序号</th>';
            headers.forEach(h => table += '<th style="width: ' + headerColWidth + ';">' + sanitizeInput(h) + '</th>');
            table += '</tr></thead><tbody>';
            rows.forEach((r, i) => {
                table += '<tr><td style="width: ' + firstColWidth + ';">' + (i + 1) + '</td>';
                headers.forEach(h => {
                    let v = r[h] || '';
                    if (Array.isArray(v)) v = v.join('\n');
                    // 将空格转换为换行符，与后端处理保持一致
                    v = v.replace(/\s+/g, '\n');
                    table += '<td class="cell-content" style="width: ' + headerColWidth + ';">' + sanitizeInput(v) + '</td>';
                });
                table += '</tr>';
            });
            table += '</tbody></table>';
            console.log(`网页表格行数: ${rows.length}`);
            return table;
        };

        // 导出功能
        const exportToExcel = async (headers, rows) => {
            if (!headers || !rows.length) return alert('无数据可导出');
            console.log(`导出Excel，传递行数: ${rows.length}`);
            showLoading();
            elements.exportStatus.textContent = '正在导出...';
            elements.exportStatus.style.display = 'block';
            try {
                // 处理数据，确保 List 值使用 \n 换行符
                const processedRows = rows.map(row => {
                    const newRow = {};
                    for (const key in row) {
                        if (Array.isArray(row[key])) {
                            newRow[key] = row[key].join('\n');
                        } else {
                            newRow[key] = row[key];
                        }
                    }
                    return newRow;
                });

                const response = await fetch('/api/export_excel', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ headers, rows: processedRows })
                });
                if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
                const blob = await response.blob();
                const filename = response.headers.get('Content-Disposition')?.split('filename=')[1]?.replace(/"/g, '') || 'result.xlsx';
                const link = document.createElement('a');
                link.href = URL.createObjectURL(blob);
                link.download = filename;
                link.click();
                elements.exportStatus.textContent = '导出完成！';
                setTimeout(() => elements.exportStatus.style.display = 'none', 2000);
            } catch (e) {
                elements.exportStatus.textContent = `导出失败: ${e.message}`;
            } finally {
                hideLoading();
            }
        };

        // 解析逻辑
        const parseText = debounce(async () => {
            const text = sanitizeInput(elements.inputText.value.trim());
            const template = elements.templateText.value.trim();
            if (!text || !template) {
                elements.result.innerHTML = '请在上方输入文本和模板，结果将实时更新';
                elements.result.classList.remove('error');
                latestData = null;
                return;
            }
            showLoading();
            try {
                const response = await fetch('/api/parse', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        text,
                        template_content: template,
                        ignore_timestamp: elements.ignoreTimestamp.checked,
                        timestamp_regex: elements.timestampRegex.value.trim()
                    })
                });
                const data = await response.json();
                if (data.error) throw new Error(data.error);
                elements.result.innerHTML = createTable(data.headers, data.rows);
                elements.result.classList.remove('error');
                latestData = data;
            } catch (e) {
                showError(`解析失败: ${e.message}`);
                latestData = null;
            } finally {
                hideLoading();
            }
        }, 300);

        // 模板管理
        const saveTemplate = () => {
            const name = sanitizeInput(elements.templateName.value.trim() || elements.templateSelect.value.trim());
            const content = elements.templateText.value.trim();
            const category = sanitizeInput(elements.templateCategoryInput.value.trim() || '未分类');
            if (!name || !content) return alert('请输入模板名称和内容');
            showModal('请输入密码以保存模板(默认密码是：123)', async password => {
                try {
                    const response = await fetch('/api/save_template', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ template_name: name, template_content: content, category, password })
                    });
                    const data = await response.json();
                    if (data.error) throw new Error(data.error);
                    updateTemplateList(data.templates);
                    elements.templateName.value = elements.templateCategoryInput.value = '';
                    alert(`模板保存成功: ${data.saved_name}`);
                } catch (e) {
                    showError(`保存失败: ${e.message}`);
                }
            });
        };

        const deleteTemplate = () => {
            const name = elements.templateSelect.value.trim();
            if (!name) return alert('请先选择要删除的模板');
            if (!confirm(`确定删除模板 "${name}" 吗？`)) return;
            showModal('请输入密码以删除模板', async password => {
                try {
                    const response = await fetch('/api/delete_template', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ template_name: name, password })
                    });
                    const data = await response.json();
                    if (data.error) throw new Error(data.error);
                    updateTemplateList(data.templates);
                    elements.templateText.value = elements.templateName.value = elements.templateCategoryInput.value = '';
                    alert(`模板 "${name}" 删除成功`);
                } catch (e) {
                    showError(`删除失败: ${e.message}`);
                }
            });
        };

        const loadTemplate = async () => {
            const name = elements.templateSelect.value.trim();
            if (!name) {
                elements.templateText.value = elements.templateName.value = elements.templateCategoryInput.value = '';
                return parseText();
            }
            showLoading();
            try {
                const response = await fetch('/api/get_template', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ template_name: name })
                });
                const data = await response.json();
                if (data.error) throw new Error(data.error);
                elements.templateText.value = data.content;
                elements.templateName.value = name;
                elements.templateCategoryInput.value = data.category;
                parseText();
            } catch (e) {
                showError(`加载失败: ${e.message}`);
            } finally {
                hideLoading();
            }
        };

        const updateTemplateList = templates => {
            elements.templateSelect.innerHTML = '<option value="">选择已有模板</option>';
            const categories = new Set(templates.map(t => t.category));
            templates.forEach(t => {
                const opt = document.createElement('option');
                opt.value = t.name;
                opt.text = `${t.name} (${t.category})`;
                opt.dataset.category = t.category;
                elements.templateSelect.appendChild(opt);
            });
            elements.templateCategory.innerHTML = '<option value="">所有分类</option>';
            categories.forEach(cat => {
                const opt = document.createElement('option');
                opt.value = opt.text = cat;
                elements.templateCategory.appendChild(opt);
            });
            filterTemplates();
        };

        const filterTemplates = () => {
            const cat = elements.templateCategory.value;
            Array.from(elements.templateSelect.options).forEach(opt => {
                opt.style.display = !cat || opt.dataset.category === cat || opt.value === '' ? '' : 'none';
            });
        };

        // 主题切换
        const toggleTheme = () => {
            document.body.classList.toggle('dark-mode');
            localStorage.setItem('theme', document.body.classList.contains('dark-mode') ? 'dark' : 'light');
            elements.themeToggle.innerHTML = `<i class="fas fa-${document.body.classList.contains('dark-mode') ? 'sun' : 'moon'}"></i>`;
        };

        // 调试模式切换
        elements.debugMode.addEventListener('change', async () => {
            try {
                const response = await fetch('/api/set_log_level', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        debug_mode: elements.debugMode.checked
                    })
                });
                const data = await response.json();
                if (data.error) throw new Error(data.error);
                elements.exportStatus.textContent = data.debug_mode ? '已开启调试模式' : '已关闭调试模式';
                elements.exportStatus.style.display = 'block';
                setTimeout(() => elements.exportStatus.style.display = 'none', 2000);
            } catch (e) {
                elements.exportStatus.textContent = `设置失败: ${e.message}`;
                elements.exportStatus.style.display = 'block';
            }
        });

        // 事件绑定
        elements.inputText.addEventListener('input', parseText);
        elements.templateText.addEventListener('input', parseText);
        elements.templateSelect.addEventListener('change', loadTemplate);
        elements.saveTemplateBtn.addEventListener('click', saveTemplate);
        elements.deleteTemplateBtn.addEventListener('click', deleteTemplate);
        elements.exportExcelBtn.addEventListener('click', () => exportToExcel(latestData?.headers, latestData?.rows));
        elements.templateCategory.addEventListener('change', filterTemplates);
        elements.ignoreTimestamp.addEventListener('change', parseText);
        elements.timestampRegex.addEventListener('input', parseText);
        elements.themeToggle.addEventListener('click', toggleTheme);
        elements.modalConfirm.addEventListener('click', () => modalCallback?.(elements.passwordInput.value) && hideModal());
        elements.modalCancel.addEventListener('click', hideModal);
        elements.passwordInput.addEventListener('keypress', e => e.key === 'Enter' && elements.modalConfirm.click());

        // 初始化
        updateTemplateList({{ templates | tojson }});
        if (localStorage.getItem('theme') === 'dark') toggleTheme();
    </script>
</body>
</html> 